#!/bin/bash

################################################################################
# 数据库自动备份脚本
# 用途: 备份 MongoDB, PostgreSQL, Redis 数据
# 作者: AI Writing Platform Team
# 日期: 2025-10-14
################################################################################

set -e  # 遇到错误立即退出

# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

# 配置
BACKUP_ROOT="/mnt/BackupData/aipaper/databases"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="$BACKUP_ROOT/$TIMESTAMP"
KEEP_DAYS=7  # 保留最近7天的备份

# MongoDB 配置
MONGO_HOST="localhost"
MONGO_PORT="27017"
MONGO_USER="admin"
MONGO_PASS="newhand"
MONGO_AUTH_DB="admin"
MONGO_DATABASES=("ai_writing" "crawler_db")

# PostgreSQL 配置
POSTGRES_HOST="localhost"
POSTGRES_PORT="15432"
POSTGRES_USER="admin"
POSTGRES_PASS="newhand"
POSTGRES_DB="aipaper_db"

# Redis 配置
REDIS_HOST="localhost"
REDIS_PORT="6379"

################################################################################
# 函数定义
################################################################################

log_info() {
    echo -e "${GREEN}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
}

log_warn() {
    echo -e "${YELLOW}[WARN]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
}

log_error() {
    echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
}

# 创建备份目录
create_backup_dir() {
    log_info "创建备份目录: $BACKUP_DIR"
    mkdir -p "$BACKUP_DIR"/{mongodb,postgresql,redis}
}

# 备份 MongoDB
backup_mongodb() {
    log_info "开始备份 MongoDB..."

    for db in "${MONGO_DATABASES[@]}"; do
        log_info "  备份数据库: $db"
        docker exec mongodb mongodump \
            --host "$MONGO_HOST" \
            --port "$MONGO_PORT" \
            --username "$MONGO_USER" \
            --password "$MONGO_PASS" \
            --authenticationDatabase "$MONGO_AUTH_DB" \
            --db "$db" \
            --out "/tmp/backup_$TIMESTAMP" \
            2>&1 | grep -v "warning"

        # 复制到宿主机
        docker cp mongodb:/tmp/backup_$TIMESTAMP/$db "$BACKUP_DIR/mongodb/"

        # 清理容器内的临时备份
        docker exec mongodb rm -rf "/tmp/backup_$TIMESTAMP"

        # 统计文档数
        local doc_count=$(docker exec mongodb mongosh \
            --quiet \
            --username "$MONGO_USER" \
            --password "$MONGO_PASS" \
            --authenticationDatabase "$MONGO_AUTH_DB" \
            "$db" \
            --eval "db.stats().dataSize" 2>/dev/null || echo "0")

        log_info "  ✅ $db 备份完成 (大小: $doc_count bytes)"
    done

    log_info "MongoDB 备份完成"
}

# 备份 PostgreSQL
backup_postgresql() {
    log_info "开始备份 PostgreSQL..."

    # 使用 pg_dump 导出
    docker exec postgres pg_dump \
        -h localhost \
        -p 5432 \
        -U "$POSTGRES_USER" \
        -d "$POSTGRES_DB" \
        -F c \
        -f "/tmp/postgres_backup_$TIMESTAMP.dump" \
        2>&1 | grep -v "warning" || true

    # 复制到宿主机
    docker cp postgres:/tmp/postgres_backup_$TIMESTAMP.dump "$BACKUP_DIR/postgresql/"

    # 清理容器内的临时备份
    docker exec postgres rm -f "/tmp/postgres_backup_$TIMESTAMP.dump"

    # 同时导出 SQL 格式（便于查看）
    docker exec postgres pg_dump \
        -h localhost \
        -p 5432 \
        -U "$POSTGRES_USER" \
        -d "$POSTGRES_DB" \
        --clean \
        --if-exists \
        > "$BACKUP_DIR/postgresql/${POSTGRES_DB}.sql" 2>/dev/null || true

    log_info "✅ PostgreSQL 备份完成"
}

# 备份 Redis
backup_redis() {
    log_info "开始备份 Redis..."

    # 触发 Redis 保存快照
    docker exec redis redis-cli SAVE > /dev/null 2>&1 || true

    # 复制 RDB 文件
    docker cp redis:/data/dump.rdb "$BACKUP_DIR/redis/" 2>/dev/null || {
        log_warn "Redis RDB 文件不存在，跳过备份"
        return
    }

    # 导出所有键值（JSON格式）
    docker exec redis redis-cli --scan | while read key; do
        type=$(docker exec redis redis-cli TYPE "$key" | tr -d '\r')
        value=$(docker exec redis redis-cli GET "$key" 2>/dev/null | tr -d '\r' || echo "")
        echo "{\"key\":\"$key\",\"type\":\"$type\",\"value\":\"$value\"}"
    done > "$BACKUP_DIR/redis/keys.json" 2>/dev/null || true

    log_info "✅ Redis 备份完成"
}

# 压缩备份
compress_backup() {
    log_info "压缩备份文件..."

    cd "$BACKUP_ROOT"
    tar -czf "${TIMESTAMP}.tar.gz" "$TIMESTAMP"

    # 获取压缩文件大小
    local size=$(du -h "${TIMESTAMP}.tar.gz" | cut -f1)
    log_info "✅ 备份已压缩: ${TIMESTAMP}.tar.gz (大小: $size)"

    # 可选：删除未压缩的目录
    # rm -rf "$TIMESTAMP"
}

# 清理旧备份
cleanup_old_backups() {
    log_info "清理 $KEEP_DAYS 天前的旧备份..."

    find "$BACKUP_ROOT" -name "*.tar.gz" -type f -mtime +$KEEP_DAYS -delete
    find "$BACKUP_ROOT" -maxdepth 1 -type d -mtime +$KEEP_DAYS ! -name "backups" -exec rm -rf {} \; 2>/dev/null || true

    local remaining=$(find "$BACKUP_ROOT" -name "*.tar.gz" -type f | wc -l)
    log_info "✅ 清理完成，当前保留 $remaining 个备份文件"
}

# 生成备份报告
generate_report() {
    local report_file="$BACKUP_DIR/backup_report.txt"

    cat > "$report_file" <<EOF
================================================================================
数据库备份报告
================================================================================
备份时间: $(date '+%Y-%m-%d %H:%M:%S')
备份目录: $BACKUP_DIR

--------------------------------------------------------------------------------
MongoDB 备份
--------------------------------------------------------------------------------
EOF

    for db in "${MONGO_DATABASES[@]}"; do
        if [ -d "$BACKUP_DIR/mongodb/$db" ]; then
            local collections=$(ls "$BACKUP_DIR/mongodb/$db"/*.bson 2>/dev/null | wc -l)
            local size=$(du -sh "$BACKUP_DIR/mongodb/$db" | cut -f1)
            echo "  ✓ $db: $collections 个集合, 大小: $size" >> "$report_file"
        fi
    done

    cat >> "$report_file" <<EOF

--------------------------------------------------------------------------------
PostgreSQL 备份
--------------------------------------------------------------------------------
EOF

    if [ -f "$BACKUP_DIR/postgresql/${POSTGRES_DB}.sql" ]; then
        local size=$(du -sh "$BACKUP_DIR/postgresql/${POSTGRES_DB}.sql" | cut -f1)
        echo "  ✓ $POSTGRES_DB: 大小: $size" >> "$report_file"
    fi

    cat >> "$report_file" <<EOF

--------------------------------------------------------------------------------
Redis 备份
--------------------------------------------------------------------------------
EOF

    if [ -f "$BACKUP_DIR/redis/dump.rdb" ]; then
        local size=$(du -sh "$BACKUP_DIR/redis/dump.rdb" | cut -f1)
        local keys=$(cat "$BACKUP_DIR/redis/keys.json" 2>/dev/null | wc -l)
        echo "  ✓ dump.rdb: 大小: $size, 键数量: $keys" >> "$report_file"
    fi

    cat >> "$report_file" <<EOF

--------------------------------------------------------------------------------
备份统计
--------------------------------------------------------------------------------
  总大小: $(du -sh "$BACKUP_DIR" | cut -f1)
  文件数: $(find "$BACKUP_DIR" -type f | wc -l)

================================================================================
EOF

    cat "$report_file"
    log_info "备份报告已生成: $report_file"
}

# 验证备份
verify_backup() {
    log_info "验证备份完整性..."

    local errors=0

    # 检查 MongoDB 备份
    for db in "${MONGO_DATABASES[@]}"; do
        if [ ! -d "$BACKUP_DIR/mongodb/$db" ]; then
            log_error "MongoDB 数据库 $db 备份失败"
            ((errors++))
        fi
    done

    # 检查 PostgreSQL 备份
    if [ ! -f "$BACKUP_DIR/postgresql/${POSTGRES_DB}.sql" ]; then
        log_warn "PostgreSQL 数据库备份文件不存在"
    fi

    if [ $errors -eq 0 ]; then
        log_info "✅ 备份验证通过"
        return 0
    else
        log_error "❌ 备份验证失败，发现 $errors 个错误"
        return 1
    fi
}

################################################################################
# 主函数
################################################################################

main() {
    log_info "=========================================="
    log_info "开始数据库备份"
    log_info "=========================================="

    # 检查 Docker 容器是否运行
    if ! docker ps | grep -q mongodb; then
        log_error "MongoDB 容器未运行"
        exit 1
    fi

    if ! docker ps | grep -q postgres; then
        log_warn "PostgreSQL 容器未运行，跳过备份"
    fi

    if ! docker ps | grep -q redis; then
        log_warn "Redis 容器未运行，跳过备份"
    fi

    # 创建备份目录
    create_backup_dir

    # 执行备份
    backup_mongodb

    if docker ps | grep -q postgres; then
        backup_postgresql
    fi

    if docker ps | grep -q redis; then
        backup_redis
    fi

    # 验证备份
    if ! verify_backup; then
        log_error "备份验证失败，请检查日志"
        exit 1
    fi

    # 生成报告
    generate_report

    # 压缩备份
    compress_backup

    # 清理旧备份
    cleanup_old_backups

    log_info "=========================================="
    log_info "✅ 数据库备份完成"
    log_info "备份位置: $BACKUP_DIR"
    log_info "压缩文件: $BACKUP_ROOT/${TIMESTAMP}.tar.gz"
    log_info "=========================================="
}

# 运行主函数
main "$@"
